3.4. 高级GDB特性

本节介绍高级 GDB 功能,其中一些功能只有在阅读了操作系统 章节后才有意义。

3.4.1. GDB和make

GDB 接受 make 命令以在调试会话期间重建可执行文件,如果构建成功,它将运行新构建的程序(当发出 run 命令时)。

(gdb) make
(gdb) run

对于设置了许多断点并修复了一个错误但想要继续调试会话的用户来说,从 GDB 中进行构建非常方便。在这种情况下,GDB 用户可以运行 make 并在仍然设置所有断点的情况下开始调试程序的新版本,而不是退出 GDB、重新编译、使用新的可执行文件重新启动 GDB 以及重置所有断点。但请记住,如果添加或删除了源代码行,则通过在 GDB 中运行 make 来修改 C 源代码并重新编译可能会导致新版本程序中的断点与旧版本中的断点不在同一逻辑位置。出现此问题时,请退出 GDB 并在新的可执行文件上重新启动 GDB 会话,或者使用 disabledelete 禁用或删除旧断点,然后使用 break 在新编译的程序版本中的正确位置设置新断点。

3.4.2. 将 GDB 附加到正在运行的进程

GDB 支持通过将 GDB 连接到正在运行的进程来调试已在运行的程序(而不是在 GDB 会话中启动程序运行)。为此,用户需要获取进程 ID (PID) 值:

  1. 使用 ps shell 命令获取进程的 PID:
    # ps to get process's PID (lists all processes started in current shell):
    $ ps
    
    # list all processes and pipe through grep for just those named a.out:
    $ ps -A | grep a.out
       PID TTY          TIME CMD
       12345 pts/3     00:00:00 a.out
    
  1. 启动GDB并将其附加到特定的运行进程(PID为12345):
    # gdb <executable> <pid>
    $ gdb a.out 12345
    (gdb)
    
    # OR alternative syntax: gdb attach <pid>  <executable>
    $ gdb attach 12345 a.out
    (gdb)

将 GDB 附加到进程会暂停该进程,用户可以在继续执行之前发出 GDB 命令。

或者,程序可以通过调用 kill(getpid(), SIGSTOP) 显式暂停自身以等待调试(如 attach_example.c 示例中所示)。当程序此时暂停时,程序员可以将 GDB 附加到进程中以对其进行调试。

无论程序如何暂停,在 GDB 连接进程并且用户输入一些 GDB 命令后,程序都会使用 cont 从其连接点继续执行。如果 cont 不起作用,GDB 可能需要显式向进程发送 SIGCONT 信号才能继续执行:

 (gdb) signal SIGCONT

3.4.3. 跟踪fork系统调用之后的进程

当GDB调试一个调用fork()函数创建新子进程的程序时,可以将GDB设置为跟随(调试)父进程或子进程,使另一个进程的执行不受GDB影响。默认情况下,GDB 在调用 fork() 后跟随父级。要将 GDB 设置为跟随子进程,请使用 set follow-fork-mode 命令:

(gdb) set follow-fork-mode child    # Set gdb to follow child on fork

(gdb) set follow-fork-mode parent   # Set gdb to follow parent on fork
(gdb) show follow-fork-mode         # Display gdb's follow mode

当用户想要在 GDB 会话期间更改此行为时,在程序中的 fork() 调用处设置断点非常有用。

attach_example.c 示例展示了一种在 fork 上"跟随"两个进程的方法:GDB 在 fork 后跟随父进程,子进程向自己发送一个 SIGSTOP 信号以在 fork 后显式暂停,从而允许程序员在继续之前将第二个 GDB 进程附加到子进程。

3.4.4. 信号控制

GDB进程可以向正在调试的目标进程发送信号,并且可以处理目标进程接收到的信号。 GDB 可以使用 signal 命令向其调试的进程发送信号:

(gdb) signal SIGCONT
(gdb) signal SIGALARM
...

有时,用户希望 GDB 在被调试进程收到信号时执行某些操作。例如,如果程序尝试访问其所访问类型的内存地址未对齐的内存,它会收到 SIGBUS 信号,并且通常会退出。 GDB 在 SIGBUS 上的默认行为也是让进程退出。但是,如果您希望 GDB 在收到 SIGBUS 时检查程序状态,则可以使用 handle 命令指定 GDB 以不同的方式处理 SIGBUS 信号( info 命令显示有关 GDB 在调试期间如何处理进程接收到的信号的附加信息):

(gdb) handle SIGBUS stop    # if program gets a SIGBUS, gdb gets control

(gdb) info signal           # list info on all signals
(gdb) info SIGALRM          # list info just for the SIGALRM signal

3.4.5. DDD设置和错误修复

运行 DDD 会在您的主目录中创建一个 .ddd 目录,用于存储其设置,以便用户无需在每次调用时从头开始重置所有首选项。保存设置的一些示例包括子窗口的大小、菜单显示选项以及启用窗口以查看寄存器值和汇编代码。

有时 DDD 在启动时挂起,并显示“等待 GDB 就绪”消息。这通常表明其保存的设置文件中有错误。解决此问题的最简单方法是删除“.ddd”目录(您将丢失所有保存的设置,并且需要在再次启动时重置它们):

$ rm -rf ~/.ddd  # Be careful when entering this command!
$ ddd ./a.out